/* 
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2010. All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:  Use,
 * duplication or disclosure restricted by GSA ADP Schedule
 * Contract with IBM Corp.
 *
 * This code is released under the terms of the Eclipse Public License
 * version 1.0 which can be found at the top level of this project or
 * or at http://www.eclipse.org/org/documents/epl-v10.php
 */

package com.ibm.mining.model;

import java.io.OutputStream;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;

import com.ibm.mining.MyAssert;

public abstract class Model {
	private Map<String, ModelClass> modelClasses = new HashMap<String, ModelClass>();
	private final Map<String, Type> types = new HashMap<String, Type>();
	
	
	public Model() {	
		/* build the primitive types in the static constructor */
		for (String name : new String[] {"String", "Integer", "Boolean", "Timestamp"}) {
			Type type = buildType(name);
			type.primitive = true;
		}
		this.buildModel();
	}
	
	public abstract void buildModel();
	
	public ModelClass getClassForName(String name) {
		if (!modelClasses.containsKey(name)) {
			throw new RuntimeException("Can't locate requested ModelClass " + name + "!");
		}
		return modelClasses.get(name);
	}
	
	public Map<String, ModelClass> getModelClasses() {
		return modelClasses;
	}
	
	
	
	public ModelClass buildModelClass(String name) {
		MyAssert.check(!modelClasses.containsKey(name), 
				"Can't create type " + name + " when it already exists!");
		ModelClass mc = new ModelClass(name, this);
		modelClasses.put(name, mc);
		return mc;
	}
	
	public ModelClass getModelClass(String name) {
		/* Often we'll use this in queries, which are case insensitive,
		 * so we want to check in a case insensitive way.  This makes it
		 * O(n) rather than O(1), but n is small so we don't care ;)
		 */
		for (String keyName : modelClasses.keySet()) {
			if (keyName.equalsIgnoreCase(name)) {
				return modelClasses.get(keyName);
			}
		}
		throw new RuntimeException("Cannot find ModelClass " + name);
	}
	
	public Type buildType(String name, Integer length) {
		String completeName = name;
		if (length != null) {
			completeName += "(" + length + ")";
		}
		MyAssert.check(!types.containsKey(completeName), "Can't create type " + name + " when it already exists!");
		Type type = new Type(name, length, this);
		types.put(completeName, type);
		if (name.equals("String")) {
			type.primitive = true;
		} else {
			type.primitive = false;		
		}
		return type;
	}
	
	public Type buildType(String name) {
		return buildType(name, null);
	}
	
	public Type getType(String name, Integer length) {
		String completeName = name;
		if (length != null) {
			completeName += "(" + length + ")";
		}
		MyAssert.check(types.containsKey(completeName), "Can't find requested type " + completeName);
		return types.get(completeName);
	}
	
	public Type getType(String name) {
		return getType(name, null);
	}
	
	public void output(PrintStream out) {output(out, "");}
	
	public void output(PrintStream out, String indent) {
		out.println(indent + "Model:");
		for (ModelClass mc : modelClasses.values()) {
			mc.output(out, indent + "  ");
		}
		
	}
	
}
